import 'dart:io';
import 'package:flutter/services.dart';

///@date:  2021/02/24
///@author:  lixu
///@description:  xlog日志打印
class XLogUtils {
  XLogUtils._();

  static const MethodChannel _channel = const MethodChannel('com.qw.flutter.xlog.plugins/xlog');

  static bool _isAndroidPlatform = Platform.isAndroid;

  ///日志分割长度
  static const int _maxLen = 800;

  static String _tag = 'xlog_plugin';

  ///是否拦截ios调用native打印日志
  static bool _isInterceptIosNativeCall = true;

  ///ios调用打印log时，回调该方法
  static Function(String level, String tag, String msg)? _onIosLogCallback;

  ///ios调用appenderFlush时，回调该方法
  static Function()? _onIosAppenderFlushCallback;

  ///ios调用dispose时，回调该方法
  static Function()? _onIosDisposeCallback;

  ///xlog日志工具初始化
  ///日志保存到本地文件的前提是：Android平台+isUseFlutterPrintMethodLog为false；
  ///[tag] 全局日志tag
  ///[isConsolePrintLog] 控制台是否打印日志
  ///[saveLogFilePath] log文件保存目录
  ///[encryptPubKey] 日志文件保存到本地，加密公钥，为空表示日志文件不加密
  ///[isInterceptIosNativeCall] 是否拦截ios调用native打印日志
  ///[onIosInitCallback] 为了兼容iOS打印日志，适配：ios调用初始化时，且isInterceptIosNativeCall=true回调该方法
  ///[onIosLogCallback] 为了兼容iOS打印日志，适配：ios调用打印log时，回调该方法
  ///[onIosAppenderFlushCallback] 为了兼容iOS打印日志，适配：ios调用appenderFlush时，回调该方法
  ///[onIosDisposeCallback] 为了兼容iOS打印日志，适配：ios调用dispose时，回调该方法
  static Future<void> init({
    required String tag,
    required bool isConsolePrintLog,
    required String? saveLogFilePath,
    required String? encryptPubKey,
    bool isInterceptIosNativeCall = true,
    Function()? onIosInitCallback,
    Function(String level, String tag, String msg)? onIosLogCallback,
    Function()? onIosAppenderFlushCallback,
    Function()? onIosDisposeCallback,
  }) async {
    _tag = tag;
    _onIosLogCallback = onIosLogCallback;
    _onIosAppenderFlushCallback = onIosAppenderFlushCallback;
    _onIosDisposeCallback = onIosDisposeCallback;
    _isInterceptIosNativeCall = isInterceptIosNativeCall;

    if (_isAndroidPlatform || !_isInterceptIosNativeCall) {
      ///android 使用native打印日志：初始化native xlog库
      await _channel.invokeMethod('init', {
        'logTag': _tag,
        'isConsoleLogOpen': isConsolePrintLog,
        'logPath': saveLogFilePath,
        'encryptPubKey': encryptPubKey,
      });
    } else {
      ///iOS初始化方法回调
      onIosInitCallback?.call();
    }
  }

  static void v(String tag, String? msg) {
    _log('v', tag, msg);
  }

  static void d(String tag, String? msg) {
    _log('d', tag, msg);
  }

  static void i(String tag, String? msg) {
    _log('i', tag, msg);
  }

  static void w(String tag, String? msg) {
    _log('w', tag, msg);
  }

  static void e(String tag, String? msg) {
    _log('e', tag, msg);
  }

  static Future<void> _log(String level, String tag, String? msg) async {
    if (_isAndroidPlatform || !_isInterceptIosNativeCall) {
      ///Android 使用xlog打印日志
      await _channel.invokeMethod('log', {'level': level, 'tag': tag, 'msg': msg});
    } else {
      ///iOS打印日志
      if (_onIosLogCallback == null || msg == null) {
        _flutterPrint(level, '${_tag}_$level: $tag', msg);
      } else {
        _onIosLogCallback?.call(level, tag, msg);
      }
    }
  }

  ///flutter打印日志
  static void _flutterPrint(String method, String? tag, String? log) {
    String msg = log?.toString() ?? '_flutterPrint log is null';
    if (msg.length <= _maxLen) {
      print('$tag: $msg');
    } else {
      print('$tag ======================== log start ================================');
      while (msg.isNotEmpty) {
        if (msg.length > _maxLen) {
          print('${msg.substring(0, _maxLen)}');
          msg = msg.substring(_maxLen, msg.length);
        } else {
          print('$msg');
          msg = '';
        }
      }
      print('$tag ======================== log end ================================');
    }
  }

  ///调用该接口会把内存中的日志写入到文件
  static Future<void> appenderFlush() async {
    if (_isAndroidPlatform || !_isInterceptIosNativeCall) {
      await _channel.invokeMethod('appenderFlush');
    } else {
      _onIosAppenderFlushCallback?.call();
    }
  }

  static Future<void> dispose() async {
    if (_isAndroidPlatform || !_isInterceptIosNativeCall) {
      await _channel.invokeMethod('onDestroy');
    } else {
      _onIosDisposeCallback?.call();
    }
  }
}
